// https://blog.csdn.net/weixin_39212021/article/details/78848659
/*
二、一些基础
    上接： cuda并行编程基础（一）
    （二）cuda基础：gridBlock.cu
        4.总计算量与block、grid的关系
            假设一维数组总计算量为total_c，怎么确定block与grid呢?
            一维数组：
                int cal_array[XX];
                int thd_num=16;
                dim3 block(thd_num);//一般block.x * block.y * block.z <=1024，1024这个值根据显卡性能确定
                dim3 grid((XX + thd_num -1)/thd_num);//这里为何这么计算呢？
            之所以grid.x = XX + thd_num - 1 /thd_num，原因是要保证block*grid >=XX
            二维数组：
                int cal_array[XX][YY];
                int thd_x_num=16,thd_y_num=32;
                dim3 block(thd_x_num,thd_y_num);//一般block.x * block.y * block.z <=1024，1024这个值根据显卡性能确定
                dim3 grid((XX + thd_x_num -1)/thd_x_num,(YY + thd_y_num -1)/thd_y_num);//原因同一维数组
            依次类推，读者可以确定三维数组的计算方法，至于思维数组怎么办？这个嘿嘿，自己想办法啦。
            问题来了，这样的话，grid和block肯定有很多分配方法，对计算会有什么影响吗？答案是肯定的，别着急，以后再学啦，
        5.内存管理
            当然是GPU的内存，跟CPU一样，GPU的内存资源也有限制，申请内存太多会爆掉哦，
            GPU内存分为共享内存和全局内存，所谓共享内存嘛，就是线程可以共享，全局嘛，所有都能访问咯
                CPU：malloc  memcpy  memset  free
                GPU: cudaMalloc  cudaMemcpy  cudaMemset  cudaFree
                cudaError_t cudaMalloc(void **devPtr,size_t size);//只能对GPU数据
                cudaError_t cudaMemcpy(void *dst,const void *src,size_t count,cudaMemcpyKind kind);//可以对GPU也可以对CPU或混合
                cudaError_t cudaMemset(void *devPtr,int value,size_t count);//只能对GPU数据
                cudaError_t cudaFree(void *devPtr);//只能对GPU数据
            可以看到，GPU的内存管理函数相比CPU，无非加了“cuda”，还是很简单的。
            cudaError_t：正确的话返回cudaSuccess，错误返回cudaErrorMemoryAllocation,你可以参看"cuda并行编程基础（一）",或者后面单独对错误处理的章节
            cudaMemcpyKind可以有四个值哦：
                cudaMemcpyHostToHost //CPU到CPU，相当与memcpy
                cudaMemcpyHostToDevice //CPU到GPU
                cudaMemcpyDeviceToHost //GPU到CPU
                cudaMemcpyDeviceToDevice //GPU到GPU  
                */

#include <stdio.h>
#include<stdlib.h>
                //下面两个头文件基本都会用到，别省了
#include "cuda_runtime.h"
#include "device_launch_parameters.h"

//这个宏还是蛮有用,主要功能是检测cuda函数的错误，以方便检查
/*
  1.cudaError_t:CUDA的错误码，它的错误码太多了，大概有80-90个，具体什么错误碰到了再解释
  2.cudaGetErrorString():将CUDA错误码转换成具体错误语句，方便判断程序员错误
  3.__FILE__,__LINE__:这个就不解释了，虽然我也用的不太熟
*/
#define CHECK(call)                                                  \
{                                                                    \
    const cudaError_t err = call;                                    \
    if (err != cudaSuccess)                                          \
    {                                                                \
        fprintf(stderr, "Error: %s:%d, ", __FILE__, __LINE__);       \
        fprintf(stderr, "code: %d, reason: %s\n", err,               \
                cudaGetErrorString(err));                            \
        exit(1);                                                     \
    }                                                                \
}



                //gridBlock.cu：block和grid的分配方法，一维数组
                /*Authored by alpc40*/
int main3(int argc, char** argv)
{
    // 定义总计算量
    int nElem = 1024;
    // 第一种分配法
    dim3 block(1024);
    dim3 grid((nElem + block.x - 1) / block.x);
    printf("grid.x %d block.x %d \n", grid.x, block.x);
    // 第二种分配法
    block.x = 512;
    grid.x = (nElem + block.x - 1) / block.x;
    printf("grid.x %d block.x %d \n", grid.x, block.x);
    // 第三种分配法
    block.x = 256;
    grid.x = (nElem + block.x - 1) / block.x;
    printf("grid.x %d block.x %d \n", grid.x, block.x);
    // 第四种分配法
    block.x = 128;
    grid.x = (nElem + block.x - 1) / block.x;
    printf("grid.x %d block.x %d \n", grid.x, block.x);
    // 恢复GPU
    CHECK(cudaDeviceReset());
    return(0);
}
